import pygame
from typing import Tuple, List

from game.container import CONSTANT


class SoundManager:
    def __init__(
        self,
        channel: pygame.mixer.Channel | None = None,
        sounds: Tuple[pygame.mixer.Sound, ...] = (),
        defualt_index: int = 0
    ):
        self.channel: pygame.mixer.Channel = channel # type: ignore
        
        self.sounds = sounds
        self.n_sounds = len(self.sounds)
        self.index = defualt_index
        
    def is_available(self) -> bool:
        if (self.channel is None) or (self.n_sounds == 0):
            return False
        return True
    
        
    def next(self) -> None:
        if not self.is_available(): return
        
        self.index = (self.index + 1) % self.n_sounds
        
        
    def play_index(
        self,
        index: int,
        loops: int = 0,
        maxtime: int = 0,
        fade_ms: int = 0
    ) -> None:
        if not self.is_available(): return
        
        self.channel.play(self.sounds[index], loops=loops, maxtime=maxtime, fade_ms=fade_ms)
        
        
    def play(
        self,
        loops: int = 0,
        maxtime: int = 0,
        fade_ms: int = 0
    ) -> None:
        if not self.is_available(): return
        
        self.play_index(self.index, loops=loops, maxtime=maxtime, fade_ms=fade_ms)
        
        
    def play_next(
        self,
        loops: int = 0,
        maxtime: int = 0,
        fade_ms: int = 0
    ) -> None:
        if not self.is_available(): return
        
        self.next()
        self.play_index(self.index, loops=loops, maxtime=maxtime, fade_ms=fade_ms)
        
        
    def pause(self) -> None:
        if not self.is_available(): return
        self.channel.pause()
        
        
    def stop(self) -> None:
        if not self.is_available(): return
        
        self.channel.stop()

    def unpause(self) -> None:
        if not self.is_available(): return
        self.channel.unpause()
        
        
    def is_channel_busy(self) -> bool:
        if not self.is_available(): return False
        
        return self.channel.get_busy()
    

class _ChannelManager:
    
    def __init__(self):
        self.n_channels = 0
        self.channels: List[pygame.mixer.Channel] = []
        
    def build(self, id: int | None = None) -> pygame.mixer.Channel:
        self.n_channels += 1
        channel = pygame.mixer.Channel(id or self.n_channels)
        self.channels.append(channel)
        return channel
    
    def set_volume_all(self, value: float): # type: ignore
        for channel in self.channels:
            channel.set_volume(value)
    

# Singleton
ChannelManager = _ChannelManager()

MenuMusicChannel = ChannelManager.build()
GameMusicChannel = ChannelManager.build()
SoundEffectChannel = ChannelManager.build()

MenuMusicManager = SoundManager(
    MenuMusicChannel,
    (CONSTANT.APP.main_menu_music, )
)
GameMusicManager = SoundManager(
    GameMusicChannel,
    (CONSTANT.APP.game_music1, CONSTANT.APP.game_music2)
)
SoundEffectManager = SoundManager(
    GameMusicChannel
)

